use crate::channel::channel;
use std::thread;

use crate::{Receiver, Sender};

pub struct Pipeline<Output> where Output:Send + 'static {
    next_recv:Receiver<Output>,
}

impl<Output:Send+'static> Pipeline<Output> {
    pub fn new<F>(f:F) -> Self 
            where F:Fn(Sender<Output>)->() + Send + 'static {
        let (tx,rx) = channel();
        thread::spawn(move||{f(tx)});
        Self {
            next_recv:rx,
        }
    }

    pub fn pipe<NewOutput:Send,F>(self,f:F) -> Pipeline<NewOutput>
            where F:Fn(Sender<NewOutput>,Receiver<Output>)->() + Send + 'static {
        let (tx,rx) = channel();
        let new_entry = Pipeline { next_recv:rx};
        thread::spawn(move ||{ f(tx,self.next_recv) });
        new_entry
    }

    pub fn end<F>(self,f:F) -> std::thread::Result<()> where F:Fn(Receiver<Output>)-> () + Send + 'static {
        thread::spawn(move ||{ f(self.next_recv) }).join()?;
        Ok(())
    }
}

impl<Output:Send+'static> IntoIterator for Pipeline<Output> {
    type Item = Output;
    type IntoIter = std::sync::mpsc::IntoIter<Output>;

    fn into_iter(self) -> Self::IntoIter {
        self.next_recv.into_iter()
    }
}